#pragma once

#include <iostream>
#include <memory>
#include <poll.h>

#include "Socket.hpp"
#include "Log.hpp"
#include "Inet_Addr.hpp"

using namespace LogModule;
using namespace SocketModule;

class PollServer
{
    const static int gnum = 1024;
    const static int gdefault = -1;

private:
    void HandlerEvents()
    {
        for (int i = 0; i < gnum; i++)
        {
            // this fd not ready
            if (_fd_events[i].fd == gdefault)
                continue;
            // this fd haved ready
            if (_fd_events[i].revents & POLLIN)
            {
                // this fd equal listenfd
                if (_fd_events[i].fd == _listenfd->Sockfd())
                {
                    // execute accepter functional
                    Accepter();
                }
                else
                {
                    // this fd is IO fd, execute HandlerIO functional
                    HandlerIO(i);
                }
            }
        }
    }

    void Accepter()
    {
        Inet_Addr addr; // storage client info
        SockSPtr sock = _listenfd->Accepter(&addr);
        // add the fd of sock into _fd_events
        if(sock->Sockfd() > 0)
        {
            LOG(DEBUG, "get a new link, client info %s:%d\n", addr.Ip().c_str(), addr.Port());
            bool flag = false; // record wheher add successly
            for (int i = 1; i < gnum; i++)
            {
                if (_fd_events[i].fd == gdefault)
                {
                    flag = true;
                    _fd_events[i].fd = sock->Sockfd();
                    _fd_events[i].events = POLLIN; // set notice event is read
                    break;
                }
    
                if (!flag)
                {
                    // close fd or the _fd_events capacity expansion
                    sock->Close();
                }
            }
        }
    }

    void HandlerIO(int index)
    {
        char buffer[gnum];
        ssize_t n = ::recv(_fd_events[index].fd, buffer, sizeof(buffer) - 1, 0);
        if (n > 0) // readf successly
        {
            buffer[n] = 0;
            // construct a simple http message
            std::string content = "<html><body><h1>hello poll</h1></body></html>";
            std::string echo_str = "HTTP/1.0 200 OK\r\n";
            echo_str += "Content-Type: text/html\r\n";
            echo_str += "Content-Length: " + std::to_string(content.size()) + "\r\n\r\n";
            echo_str += content;
            ::send(_fd_events[index].fd, echo_str.c_str(), echo_str.size(), 0); // interim programme
        }
        else if (n == 0)
        {
            // client quit
            LOG(INFO, "client quit\n");
            // OS not care the fd
            ::close(_fd_events[index].fd);
            _fd_events[index].fd = gdefault;
            _fd_events[index].events = 0;
            _fd_events[index].revents = 0;
        }
        else
        {
            // recv error
            LOG(ERROR, "client error\n");
            // OS not care the fd
            ::close(_fd_events[index].fd);
            _fd_events[index].fd = gdefault;
            _fd_events[index].events = 0;
            _fd_events[index].revents = 0;
        }
    }

public:
    PollServer(uint16_t port)
        : _port(port), _listenfd(std::make_unique<TcpSocket>()), _fd_events(nullptr)
    {
        _fd_events = new struct pollfd[gnum];
        _listenfd->BuildListenSocket(_port);
    }

    void InitServer()
    {
        // init struct pollfd space
        for (int i = 0; i < gnum; i++)
        {
            _fd_events[i].fd = gdefault;
            _fd_events[i].events = 0; // defaultly, set val is 0 , express OS not notice
            _fd_events[i].revents = 0; // defaultly, set val is 0 , express OS not notice
        }
        // defaultly, set listen sockfd in _fd_event[0]
        _fd_events[0].fd = _listenfd->Sockfd();
        _fd_events[0].events = POLLIN; // POLLIN express read events wheher ready
    }

    void Loop()
    {
        int timeout = -1; // -1 express block wait
        while (true)
        {
            int n = ::poll(_fd_events, gnum, timeout);
            switch (n)
            {
            case 0: // wait overtime in timeout
                LOG(INFO, "wait timeout\n");
                break;
            case -1: // poll error
                LOG(INFO, "wait error\n");
                break;
            default: // wait successly n ready events
                HandlerEvents();
                LOG(INFO, "have %d events ready\n", n);
                break;
            }
        }
    }

    ~PollServer()
    {
    }

private:
    uint16_t _port;                    // server port
    std::unique_ptr<Socket> _listenfd; // socket object
    struct pollfd *_fd_events;         // poll fd set
};